<?php

namespace App\Http\Controllers\H5;

use App\Common\Utils\Helper;
use App\Exports\AgentSelfEmployedExport;
use App\Http\Controllers\Controller;
use App\Jobs\DeliveryCallbackJob;
use App\Mail\AgentSelfEmployedExportMail;
use App\Models\AfterSaleOrder;
use App\Models\Agent;
use App\Models\AgentRevenue;
use App\Models\Client;
use App\Models\Order;
use App\Models\SummaryBase;
use App\Services\Order\AfterSale\HeavyShipment;
use App\Services\Order\AfterSale\MakeUp;
use App\Services\Order\AfterSale\RefundOnly;
use App\Services\Order\AfterSale\ReturnOfGoods;
use App\Services\Order\CancelOrder;
use App\Services\Order\OrderContext;
use App\Services\Order\PlaceOrder;
use Illuminate\Cache\RateLimiter;
use Illuminate\Database\Eloquent\Builder;
use Illuminate\Database\Eloquent\Relations\BelongsTo;
use Illuminate\Database\Eloquent\Relations\HasMany;
use Illuminate\Database\Eloquent\Relations\MorphMany;
use Illuminate\Database\Eloquent\Relations\MorphToMany;
use Illuminate\Http\Request;
use Illuminate\Support\Arr;
use Illuminate\Support\Carbon;
use Illuminate\Support\Facades\Http;
use Maatwebsite\Excel\Excel;
use Symfony\Component\HttpKernel\Exception\HttpException;

class OrderController extends Controller
{
    /**
     * 代理商 下单
     */
    public function store(Request $request)
    {
        // 下单频率控制
        $rateLimiter = app()->make(RateLimiter::class);
        $rateLimiterKey = sprintf('agent_%d_place_order', $this->user->getKey());
        if ($rateLimiter->tooManyAttempts($rateLimiterKey, 1)) {
            $this->response->errorForbidden('操作的太快啦');
        }
        $rateLimiter->hit($rateLimiterKey, 5); // 限流 五秒内

        $client = null;
        try {
            $client = Client::query()
                ->with(['province', 'city', 'area'])
                ->where(['id' => $request->get('client_id'), 'agent_id' => $this->user->getKey()])
                ->firstOrFail();
        } catch (\Exception $e) {
            $this->response->errorBadRequest('获取客户信息失败');
        }

        try {
            (new OrderContext(
                new PlaceOrder(
                    Agent::query()
                        ->with(['affiliatedPartner.partnerIdentity', 'directInviter', 'indirectInviter'])
                        ->findOrFail($this->user['id']),
                    $client,
                    arrayCombineByKey($request->get('sku_list'), 'sku_id'),
                    $request->get('remark', '')
                )))
                ->excuteOperation();
        } catch (\Exception $e) {
            $this->response->errorBadRequest($e->getMessage());
        }

        return $this->response->created(null);
    }

    /**
     * 订单详情
     * @param $id
     * @return mixed
     */
    public function info($id)
    {
        return $this->response->array(
            Order::query()
                ->with(['skus', 'afterSaleOrder.afterSaleOrderSkus', 'courierInformation.courierCompany', 'commodityActivity' => fn(BelongsTo $belongsTo) => $belongsTo->withTrashed()])
                ->where('agent_id', $this->user['id'])
                ->findOrFail($id)
                ->toArray()
        );
    }

    /**
     * 取消订单
     * @param $id
     * @return \Dingo\Api\Http\Response
     */
    public function cancel($id)
    {
        try {
            $order = Order::query()
                ->with([
                    'skus',
                    'commodityActivity' => fn(BelongsTo $belongsTo) => $belongsTo->withTrashed()
                ])
                ->where('agent_id', $this->user['id'])
                ->findOrFail($id);

            (new OrderContext(new CancelOrder()))->excuteOperation($order);

            // 回调取消订单通知
            if (!empty($order['delivery_callback'])) {
                dispatch((new DeliveryCallbackJob([
                    'numbering' => $order['numbering'],
                    'delivery_callback' => $order['delivery_callback'],
                    'status' => Order::STATUS_CANCELLED
                ]))->onQueue('delivery_callback'));
            }
        } catch (\Exception $e) {
            $this->response->errorBadRequest($e->getMessage());
        }

        return $this->response->noContent();
    }

    /**
     * 自营订单
     * @return mixed
     */
    public function selfEmployedList(Request $request)
    {
        if ($request->has('export')) {
            validator(
                $request->all(),
                [
                    'email' => 'required|email:rfc,dns',
                    'created_at_begin' => 'required|date|before:today',
                    'created_at_end' => 'required|date|after:created_at_begin',
                ],
                [
                    'email.required' => '请填写邮箱地址',
                    'email.email' => '请正确填写邮箱地址',
                    'created_at_begin.required' => '请选择开始时间',
                    'created_at_begin.date' => '开始时间必须是一个日期',
                    'created_at_begin.before' => '开始时间必须在今天之前',
                    'created_at_end.required' => '请选择结束时间',
                    'created_at_end.date' => '结束时间必须是一个日期',
                    'created_at_end.after' => '结束时间必须在开始时间之后',
                ]
            )->validate();

            // 验证日期差不过六个月
            if (($diffInDays = Carbon::parse($request->created_at_begin)->diffInDays(Carbon::parse($request->created_at_end))) > (30 * 6)) {
                abort(400, '请选择小于六个月的时间范围,当前日期相差' . $diffInDays . '天');
            }

            return (new AgentSelfEmployedExport($request))
                ->chain([
                    (new AgentSelfEmployedExportMail())
                        ->subject('[省到家]自营订单导出')
                        ->to($request->get('email'), \Auth::user()['name'] ?? '')
                        ->onQueue('emails')
                ])
                ->setNeedSendMail(true)
                ->download(sprintf('自营订单_%s_%s.csv', \Auth::id(), Carbon::now()->format('YmdHis')), Excel::CSV);
        }

        return $this->response->array(
            Helper::dealWithNotCompatibleSimplePaginate(
                tap(
                    Order::query()
                        ->select(['id', 'commodity_id', 'commodity_activity_id'])
                        ->with([
                            'skus',
                            'orderDetail',
                            'courierInformation.courierCompany',
                            'commodityActivity' => fn(BelongsTo $belongsTo) => $belongsTo->withTrashed()
                        ])
                        ->where('agent_id', $this->user->getKey())
                        ->when($request->get('status'), function (Builder $builder, $status) {
                            $builder->where('status', $status);
                        })
                        ->when($request->get('search_name'), function (Builder $builder, $searchName) {
                            $builder
                                ->where(function (Builder $builder) use ($searchName) {
                                    $builder
                                        ->where('client_name', 'like', "%$searchName%")
                                        ->orWhere('client_phone', 'like', "%$searchName%")
                                        ->orWhere('commodity_name', 'like', "%$searchName%");
                                });
                        })
                        ->when($request->get('created_at_begin'), function (Builder $query, $createdAtBegin) {
                            $query->where('order_date', '>=', $createdAtBegin);
                        })
                        ->when($request->get('created_at_end'), function (Builder $query, $createdAtEnd) {
                            $query->where('order_date', '<=', $createdAtEnd);
                        })
                        ->simplePaginate(),
                    function (&$orderList) {
                        // 懒加载订单详情, 单独处理订单详情合并, 优化临时表数据过多的问题
                        $tempList = $orderList->toArray();
                        $tempList['data'] = array_map(function ($item) {
                            $orderDetail = $item['order_detail'];
                            Arr::forget($item, ['id', 'commodity_id', 'commodity_activity_id', 'order_detail']);
                            return array_merge($item, $orderDetail);
                        }, $tempList['data'] ?? []);

                        $orderList = $tempList;
                    }
                )
            )
        );
    }

    /**
     * 团队订单
     * @return mixed
     */
    public function teamList(Request $request)
    {
        /** @var MorphToMany $userOrders */
        $userOrders = $this->user->relatedOrders();

        return $this->response->array(
            Helper::dealWithNotCompatibleSimplePaginate(
                tap(
                    $userOrders
                        ->withoutGlobalScope('orderByIdDesc')
                        ->select(['orders.id', 'orders.agent_id'])
                        ->with('orderDetail')
                        ->wherePivot('type', Agent::AGENT_ORDER_IDENTITY_TEAM)
                        ->when($request->get('search_name'), function (Builder $builder, $searchName) {
                            $builder
                                ->join('agents', 'agents.id', '=', 'orders.agent_id')
                                ->where(function (Builder $builder) use ($searchName) {
                                    if (is_numeric($searchName)) {
                                        $builder->where('agents.phone', 'like', "$searchName%");
                                    } else {
                                        $builder->where('agents.name', 'like', "$searchName%");
                                    }
                                });
                        })
                        ->with(['agent', 'agentRevenues' => function (MorphMany $builder) {
                            $builder->where([
                                'agent_id' => $this->user['id'],
                                'revenues_type' => AgentRevenue::REVENUES_TYPE_INCOME
                            ]);
                        }])
                        ->when($request->get('created_at_begin'), function (Builder $query, $createdAtBegin) {
                            $query->where('orders.order_date', '>=', $createdAtBegin);
                        })
                        ->when($request->get('created_at_end'), function (Builder $query, $createdAtEnd) {
                            $query->where('orders.order_date', '<=', $createdAtEnd);
                        })
                        ->whereIn('orders.status', [Order::STATUS_TO_BE_DELIVERED, Order::STATUS_SHIPPED])
                        ->orderByDesc('model_has_order.id')
                        ->simplePaginate(null, ['orders.id']),
                    function (&$orderList) {
                        // 懒加载订单详情, 单独处理订单详情合并, 优化临时表数据过多的问题
                        $tempList = $orderList->toArray();
                        $tempList['data'] = array_map(function ($item) {
                            $orderDetail = $item['order_detail'];
                            Arr::forget($item, ['id', 'order_detail']);
                            return array_merge($item, $orderDetail);
                        }, $tempList['data'] ?? []);

                        $orderList = $tempList;
                    }
                )
            )
        );
    }

    /**
     * 售后订单
     * @return mixed
     */
    public function afterSaleList(Request $request)
    {
        return $this->response->array(
            Helper::dealWithNotCompatibleSimplePaginate(
                AfterSaleOrder::query()
                    ->with(['order', 'afterSaleOrderSkus'])
                    ->where('after_sale_orders.agent_id', $this->user['id'])
                    ->when($request->get('status'), function (Builder $builder, $status) {
                        $builder->where('after_sale_orders.status', $status);
                    })
                    ->when($request->get('search_name'), function (Builder $builder, $searchName) {
                        $builder
                            ->join('orders', 'orders.id', '=', 'after_sale_orders.order_id')
                            ->where(function (Builder $builder) use ($searchName) {
                                $builder
                                    ->where('orders.client_name', 'like', "$searchName%")
                                    ->orWhere('orders.client_phone', 'like', "$searchName%")
                                    ->orWhere('orders.commodity_name', 'like', "$searchName%");
                            });
                    })
                    ->when($request->get('created_at_begin'), function (Builder $query, $createdAtBegin) {
                        $query->where('after_sale_orders.created_at', '>=', Carbon::parse($createdAtBegin)->startOfDay());
                    })
                    ->when($request->get('created_at_end'), function (Builder $query, $createdAtEnd) {
                        $query->where('after_sale_orders.created_at', '<=', Carbon::parse($createdAtEnd)->endOfDay());
                    })
                    ->select(['after_sale_orders.*'])
                    ->simplePaginate()
            )
        );
    }

    /**
     * 售后订单详情
     * @param $id
     * @return mixed
     */
    public function afterSaleInfo($id)
    {
        return $this->response->array(
            AfterSaleOrder::query()
                ->with(['order', 'attachments', 'afterSaleOrderSkus', 'audit', 'courierInformation.courierCompany'])
                ->findOrFail($id)
                ->toArray()
        );
    }

    /**
     * 取消售后
     * @param $id
     * @return \Dingo\Api\Http\Response
     */
    public function cancelSfterSale($id)
    {
        $afterSaleOrder = AfterSaleOrder::query()
            ->where([
                'agent_id' => $this->user['id'],
                'id' => $id
            ])
            ->firstOrFail();
        if ($afterSaleOrder['status'] !== AfterSaleOrder::STATUS_PENDING_REVIEW) {
            throw new HttpException(400, '只有待审核的售后才可以取消');
        }

        $afterSaleOrder->update(['status' => AfterSaleOrder::STATUS_CANCELLED]);
        return $this->response->noContent();
    }

    /**
     * 提交售后订单
     * @param Request $request
     * @return \Dingo\Api\Http\Response
     */
    public function storeSfterSale(Request $request)
    {
        try {
            $handler = null;
            switch ($request->get('type')) {
                case AfterSaleOrder::TYPE_RETURN:
                    $handler = new ReturnOfGoods($request->only(['type', 'freight', 'reason', 'uploader', 'courier_company', 'return_order_numbering', 'skus', 'picture_certificate', 'video_certificate']));
                    break;
                case AfterSaleOrder::TYPE_HEAVY_SHIPMENT:
                    $handler = new HeavyShipment($request->only(['type', 'address', 'reason', 'order_id', 'skus', 'picture_certificate', 'video_certificate']));
                    break;
                case AfterSaleOrder::TYPE_REFUND_ONLY:
                    $handler = new RefundOnly($request->only(['type', 'reason', 'order_id', 'skus', 'picture_certificate', 'video_certificate']));
                    break;
                case AfterSaleOrder::TYPE_MAKE_UP:
                    $handler = new MakeUp($request->only(['type', 'reason', 'order_id', 'compensation', 'picture_certificate', 'video_certificate']));
                    break;
                default:
                    abort(400, '请选择一种售后方式');
                    break;
            }

            $order = Order::query()
                ->with([
                    'commodityActivity' => fn(BelongsTo $belongsTo) => $belongsTo->withTrashed(),
                    'skus',
                    'afterSaleOrder' => function (HasMany $hasMany) {
                        $hasMany
                            ->with('afterSaleOrderSkus')
                            ->where('status', 'in', implode(',', [AfterSaleOrder::STATUS_PENDING_REVIEW, AfterSaleOrder::STATUS_FOLLOWING_UP, AfterSaleOrder::STATUS_PASSED]))
                            ->where('type', 'in', implode(',', [AfterSaleOrder::TYPE_RETURN, AfterSaleOrder::TYPE_HEAVY_SHIPMENT, AfterSaleOrder::TYPE_REFUND_ONLY]));
                    }])
                ->where('agent_id', $this->user['id'])
                ->findOrFail($request->get('order_id'));

            (new OrderContext($handler))->excuteOperation($order);
        } catch (\Exception $e) {
            $this->response->errorBadRequest($e->getMessage());
        }

        return $this->response->noContent();
    }

    /**
     * 统计订单
     * @param Request $request
     * @return mixed
     */
    public function statisticsList(Request $request)
    {
        // 如果是自营订单统计, 直接 order group
        if ((int) $request->get('identity_type') === SummaryBase::TYPE_AGENT) {
            return $this->response->array(
                Helper::dealWithNotCompatibleSimplePaginate(
                    Order::query()
                        ->select([
                            'commodity_id',
                            \DB::raw('count(*) as number_of_orders'),
                            \DB::raw('sum(income) as sum_of_income'),
                            \DB::raw('sum(commodity_amount) as sum_of_commodity'),
                            \DB::raw('sum(commodity_price) as sum_of_commodity_price'),
                            \DB::raw('sum(freight) as sum_of_freight'),
                            \DB::raw('sum(total_amount) as sum_of_total_amount'),
                        ])
                        ->with(['commodity.attachments'])
                        ->where('agent_id', \Auth::id())
                        ->when($request->get('search_name'), function (Builder $builder, $searchName) {
                            $builder
                                ->where('commodity_name', 'like', "$searchName%")
                                ->orWhere('commodity_numbering', 'like', "$searchName%");
                        })
                        ->when($request->get('created_at_begin'), function (Builder $builder, $createdAtBegin) {
                            $builder->where('created_at', '>=', $createdAtBegin);
                        })
                        ->when($request->get('created_at_end'), function (Builder $builder, $createdAtEnd) {
                            $builder->where('created_at', '<=', Carbon::parse($createdAtEnd)->endOfDay());
                        })
                        ->whereIn('status', [Order::STATUS_TO_BE_DELIVERED, Order::STATUS_SHIPPED])
                        ->groupBy('commodity_id')
                        ->simplePaginate()
                )
            );
        }

        // 团队订单
        if ((int) $request->get('identity_type') === SummaryBase::TYPE_TEAM) {
            return $this->response->array(
                Helper::dealWithNotCompatibleSimplePaginate(
                    \Auth::user()
                        ->relatedOrders()
                        ->with(['commodity.attachments'])
                        ->wherePivot('type', SummaryBase::TYPE_TEAM)
                        ->when($request->get('search_name'), function (Builder $builder, $searchName) {
                            $builder
                                ->where('orders.commodity_name', 'like', "$searchName%")
                                ->orWhere('orders.commodity_numbering', 'like', "$searchName%");
                        })
                        ->when($request->get('created_at_begin'), function (Builder $builder, $createdAtBegin) {
                            $builder->where('orders.created_at', '>=', $createdAtBegin);
                        })
                        ->when($request->get('created_at_end'), function (Builder $builder, $createdAtEnd) {
                            $builder->where('orders.created_at', '<=', Carbon::parse($createdAtEnd)->endOfDay());
                        })
                        ->select([
                            'orders.commodity_id',
                            \DB::raw('count(orders.commodity_id) as number_of_orders'),
                            \DB::raw('sum(orders.commodity_amount) as sum_of_commodity'),
                            \DB::raw('sum(orders.commodity_price) as sum_of_commodity_price'),
                            \DB::raw('sum(orders.freight) as sum_of_freight'),
                            \DB::raw('sum(orders.total_amount) as sum_of_total_amount'),
                            \DB::raw('sum(model_has_order.reward_summary) as reward_summary'),
                            \DB::raw('sum(model_has_order.commission_summary) as commission_summary'),
                            \DB::raw('sum(model_has_order.cultivation_awards_summary) as cultivation_awards_summary'),
                        ])
                        ->whereIn('status', [Order::STATUS_TO_BE_DELIVERED, Order::STATUS_SHIPPED])
                        ->groupBy('orders.commodity_id')
                        // 得这样做,不然 select 查询语句不对
                        ->getQuery()
                        ->simplePaginate(null, [])
                )
            );
        }

        $this->response->errorBadRequest('获取数据失败,身份类型不存在');
        return null;
    }

    public function statisticsDetailList(Request $request)
    {
        return $this->response->array(
            Helper::dealWithNotCompatibleSimplePaginate(
                Order::query()
                    ->where('commodity_id', $request->get('commodity_id'))
                    ->when($request->get('identity_type', SummaryBase::TYPE_AGENT), function (Builder $builder, $identityType) {
                        if ((int) $identityType === SummaryBase::TYPE_AGENT) {
                            $builder
                                ->with([
                                    'skus',
                                    'agentRevenues' => function (MorphMany $builder) {
                                        $builder
                                            ->where('agent_id', \Auth::id())
                                            ->where('revenues_type', AgentRevenue::REVENUES_TYPE_INCOME);
                                    },
                                    'afterSaleOrder' => function (HasMany $builder) {
                                        $builder
                                            ->with('afterSaleOrderSkus')
                                            ->where('status', AfterSaleOrder::STATUS_PASSED)
                                            ->whereIn('type', [AfterSaleOrder::TYPE_RETURN, AfterSaleOrder::TYPE_REFUND_ONLY]);
                                    }
                                ])
                                ->where('agent_id', \Auth::id());
                        } elseif ((int) $identityType === SummaryBase::TYPE_TEAM) {
                            $builder
                                ->with(['agentRevenues' => function (MorphMany $builder) {
                                    $builder->where('agent_id', \Auth::id()); // 当前用户相关的奖励
                                }])
                                ->where(function (Builder $builder) {
                                    $builder
                                        ->where('direct_inviter', \Auth::id())
                                        ->orWhere('indirect_inviter', \Auth::id());
                                });
                        } elseif ((int) $identityType === SummaryBase::TYPE_PARTNER) {
                            $builder->where('partner_id', \Auth::id());
                        }
                    })
                    ->when($request->get('created_at_begin'), function (Builder $builder, $createdAtBegin) {
                        $builder->where('created_at', '>=', $createdAtBegin);
                    })
                    ->when($request->get('created_at_end'), function (Builder $builder, $createdAtEnd) {
                        $builder->where('created_at', '<=', Carbon::parse($createdAtEnd)->endOfDay());
                    })
                    ->simplePaginate()
            )
        );
    }

    /**
     * 省到家物流地址
     */
    public function sdjLogisticsList($numbering)
    {
        return Http::get('https://checkexpress.ssddjj.com/getsn/' . $numbering)->json();
    }
}
